/*
* Copyright (c) 2023 HPMicro
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/

#include "usbd_core.h"
#include "board.h"
#include <rtdevice.h>
#include "tjpgd.h"

#define LCD_WIDTH 800
#define LCD_HEIGHT 480
#define LCD_FRAME_SIZE (LCD_WIDTH*LCD_HEIGHT*2)

#define DISPLAY_OUT_EP 0x01

#define MAX_DISP_PAYLOAD_SIZE (16384)
extern uint8_t display_read_buffer[MAX_DISP_PAYLOAD_SIZE];

static volatile uint32_t pix_index = 0;




typedef struct _udisp_frame_header_t { /* 16 bytes */
    uint16_t crc16;//payload crc16
    uint8_t  type; //raw rgb,yuv,jpg,other
    uint8_t  cmd;    
    uint16_t x;  //32bit
    uint16_t y;
    uint16_t width;//32bit
    uint16_t height;
    uint32_t frame_id:10;
    uint32_t payload_total:22; //payload max 4MB
} __attribute__((packed)) udisp_frame_header_t;


#define UDISP_TYPE_RGB565  0
#define UDISP_TYPE_RGB888  1
#define UDISP_TYPE_YUV420  2
#define UDISP_TYPE_JPG	   3


//定义 ringbuffer和信号量
#define RINGBUFFER_SIZE (1024 * 1024)
static struct rt_ringbuffer usb_rb;
__attribute__((section(".sdram"))) __attribute__((aligned(32))) static uint8_t usb_rb_buffer[RINGBUFFER_SIZE];
static struct rt_semaphore frame_sem = {0};


void usbd_graphic_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
{

    udisp_frame_header_t * pfh = (udisp_frame_header_t *)display_read_buffer;
    static int total_bytes=0xffff; /*要声明为static类型，很关键*/

    if(pix_index==0){
       //  USB_LOG_RAW("frame start");
        total_bytes = pfh->payload_total;


       //  RT_ASSERT(pfh->width==LCD_WIDTH);
       //  RT_ASSERT(pfh->height==LCD_HEIGHT);
       //  RT_ASSERT(pfh->LCD_FRAME_SIZE==LCD_HEIGHT*LCD_WIDTH*2);
       USB_LOG_RAW("pfh->wdith=%d\n", pfh->width);
       USB_LOG_RAW("pfh->height=%d\n", pfh->height);
       USB_LOG_RAW("pfh->payload_total=%d\n", pfh->payload_total);
       if(pfh->width!=LCD_WIDTH){
           pix_index = 0;
           goto __exit;
       }

        switch(pfh->type) {
           case UDISP_TYPE_RGB565:
           case UDISP_TYPE_RGB888:
           case UDISP_TYPE_YUV420:
           case UDISP_TYPE_JPG:
           {
               rt_ringbuffer_put(&usb_rb, &display_read_buffer[sizeof(udisp_frame_header_t)], nbytes - sizeof(udisp_frame_header_t));
               pix_index += nbytes - sizeof(udisp_frame_header_t);
           }
           break;
           default:
               // USB_LOG_RAW("error cmd:%x",pfh->type);
               break;
        }

        goto __exit;
    }

    rt_ringbuffer_put(&usb_rb, &display_read_buffer[0],nbytes);
    pix_index += nbytes;

    if(pix_index >= total_bytes){
        // USB_LOG_RAW("frame end");
        pix_index = 0;
        rt_sem_release(&frame_sem);
        return; /*一帧完成后, 交给线程处理数据, 由线程启动下一packet读取*/
    }

__exit:
    usbd_ep_start_read(0, ep, display_read_buffer, MAX_DISP_PAYLOAD_SIZE);
}








typedef struct {
   struct rt_ringbuffer *rb;
   uint8_t *fbuf;
   uint16_t wfbuf;
} jpg_dev_t;

static unsigned int jpg_in_func(JDEC* jd, uint8_t* buff, unsigned int nbyte)
{
   jpg_dev_t *dev = (jpg_dev_t*)jd->device;
   if (buff) {
       return rt_ringbuffer_get(dev->rb, buff, nbyte);
   } else {
       uint8_t temp_buf[64];
       unsigned int total_skip = nbyte;
       unsigned int skip_size;
       while (total_skip > 0) {
           skip_size = (total_skip > sizeof(temp_buf)) ? sizeof(temp_buf) : total_skip;
           rt_ringbuffer_get(dev->rb, temp_buf, skip_size);
           total_skip -= skip_size;
       }
       return nbyte;
   }
}

static int jpg_out_func(JDEC* jd, void* bitmap, JRECT* rect)
{
   jpg_dev_t *dev = (jpg_dev_t*)jd->device;
   uint16_t *src, *dst;
   uint16_t y, bws, bwd;
   
   src = (uint16_t*)bitmap;
   dst = (uint16_t*)(dev->fbuf + 2 * (rect->top * dev->wfbuf + rect->left));
   bws = rect->right - rect->left + 1;
   bwd = dev->wfbuf;
   
   for (y = rect->top; y <= rect->bottom; y++) {
       memcpy(dst, src, bws * 2);
       src += bws; dst += bwd;
   }
   return 1;
}








#define JPG_WORK_BUFFER_SIZE 128000
static uint8_t jpg_work_buffer[JPG_WORK_BUFFER_SIZE];


static rt_device_t lcd_device = RT_NULL;
static struct rt_device_graphic_info lcd_info;

/* BMP保存线程 */
static void udisp_thread_entry(void *parameter)
{
    rt_sem_init(&frame_sem, "frame_sem", 0, RT_IPC_FLAG_FIFO);

    rt_ringbuffer_init(&usb_rb, usb_rb_buffer, RINGBUFFER_SIZE);
    uint8_t * lcdc_buffer = rt_malloc_align(BOARD_LCD_WIDTH * BOARD_LCD_HEIGHT * 2, 32);
    RT_ASSERT(lcdc_buffer != RT_NULL);   

    lcd_device = (struct drv_lcd_device *)rt_device_find("lcd0");
    rt_device_open(lcd_device, RT_DEVICE_FLAG_RDWR);
    rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &lcd_info);
    rt_kprintf("lcd_info.width=%d\n",lcd_info.width);
    rt_kprintf("lcd_info.height=%d\n",lcd_info.height);
    rt_kprintf("lcd_info.pixel_format=%d\n",lcd_info.pixel_format);   
    
    
    JDEC jdec;
    JRESULT res;
    jpg_dev_t dev;
    
    dev.rb = &usb_rb;
    dev.fbuf = (rt_uint8_t *)lcd_info.framebuffer; /*直接解码到framebuffer中*/
    dev.wfbuf = LCD_WIDTH;


   rt_tick_t  start_tick, end_tick1, end_tick2;
    while (1) {
        start_tick = rt_tick_get();
        rt_sem_take(&frame_sem, RT_WAITING_FOREVER);
        end_tick1 = rt_tick_get();
        
        res = jd_prepare(&jdec, jpg_in_func, jpg_work_buffer, JPG_WORK_BUFFER_SIZE, &dev);
        RT_ASSERT(res == JDR_OK);

        if (res == JDR_OK) {
            res = jd_decomp(&jdec, jpg_out_func, 0);
            if (res == JDR_OK) {
                USB_LOG_RAW("decode success!\n");

                end_tick2 = rt_tick_get(); 
                rt_device_control(lcd_device, RTGRAPHIC_CTRL_RECT_UPDATE, RT_NULL);
            } else {
                rt_kprintf("JPG decode failed: %d\n", res);
            }
        }
        
        USB_LOG_RAW("elapse time1=%d ticks\n",end_tick1-start_tick);
        USB_LOG_RAW("elapse time2=%d ticks\n",end_tick2-end_tick1);   
        usbd_ep_start_read(0, DISPLAY_OUT_EP, display_read_buffer, MAX_DISP_PAYLOAD_SIZE);


        /*
            elapse time2=93 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=48464
            elapse time1=3 ticks
            elapse time2=93 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56607
            elapse time1=3 ticks
            elapse time2=98 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56619
            elapse time1=5 ticks
            elapse time2=98 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56696
            elapse time1=4 ticks
            elapse time2=96 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56770
            elapse time1=5 ticks
            elapse time2=96 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56874
            elapse time1=4 ticks
            elapse time2=96 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56892
            elapse time1=4 ticks
            elapse time2=97 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56972
            elapse time1=4 ticks
            elapse time2=99 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56995
            elapse time1=4 ticks
            elapse time2=100 ticks
            pfh->wdith=800
            pfh->height=480
            pfh->payload_total=56518
        */
   }

}



int udisp_thread_init(void)
{
    rt_thread_t udisp_thread = rt_thread_create("udisp", 
                                      udisp_thread_entry, 
                                      RT_NULL, 
                                      4096, 
                                      10, 
                                      20);
   rt_thread_startup(udisp_thread);
}

INIT_APP_EXPORT(udisp_thread_init);